using System;
using System.Reflection;

using Microsoft.SharePoint;

namespace RealDolmen.Helpers
{
    public class WrapperObject
    {
        private object wrappedObject;

        #region Constructor
        public WrapperObject(object wrappedObject)
        {
            this.wrappedObject = wrappedObject;
        } 
        #endregion // Constructor


        #region Invoke
        public object Invoke(string method)
        {
            Type type = wrappedObject.GetType();
            MethodInfo mi = type.GetMethod(method, BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[0], null);
            return mi.Invoke(wrappedObject, null);
        }

        public object Invoke(string method, params object[] parameters)
        {
            Type type = wrappedObject.GetType();
            MethodInfo mi = type.GetMethod(method, BindingFlags.Instance | BindingFlags.NonPublic);
            return mi.Invoke(wrappedObject, parameters);
        } 
        #endregion // Invoke

        #region GetMember
        public object GetMember(string key)
        {
            // Check for nested properties
            string trail = null;
            int idx = key.IndexOf('.');

            if (idx > -1)
            {
                trail = key.Substring(idx + 1);
                key = key.Substring(0, idx);
            }

            // Get the value
            object value = null;
            int idx2 = key.IndexOf('(');

            // The member should be a Property
            if (idx2 < 1)
            {
                PropertyInfo pi = wrappedObject.GetType().GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                if (pi != null)
                {
                    value = pi.GetValue(wrappedObject, null);
                }
                FieldInfo fi = wrappedObject.GetType().GetField(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
                if (fi != null)
                {
                    value = fi.GetValue(wrappedObject);
                }
            }
            // The member should be a method
            else
            {
                string methodName = key.Substring(0, idx2);
                MethodInfo mi = wrappedObject.GetType().GetMethod(methodName);
                if (mi != null)
                {
                    string strParameters = key.Substring(idx2 + 1);
                    strParameters = strParameters.Substring(0, strParameters.Length - 1);

                    string[] paramas = strParameters.Split(',');
                    object[] parameters = new object[] { paramas.Length };

                    for (int i = 0; i < paramas.Length; i++)
                    {
                        string param = paramas[i].Trim();
                        if (param[0] == '\'' || param[0] == '"')
                            parameters[i] = param.Substring(1, param.Length - 2);
                        else
                            parameters[i] = null;
                    }

                    value = mi.Invoke(wrappedObject, parameters);
                }
            }

            // Process the value
            if (value != null && !string.IsNullOrEmpty(trail))
            {
                WrapperObject wrapper = new WrapperObject(value);
                return wrapper.GetMember(trail);
            }
            return value;
        } 
        #endregion // GetMember

        #region SetField
        public void SetField(string key, object value)
        {
            Type type = wrappedObject.GetType();

            FieldInfo fi = type.GetField(key, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

            if (fi != null)
            {
                fi.SetValue(
                    wrappedObject,
                    Helper.ConvertToType(value, fi.FieldType));
            }
        } 
        #endregion // SetField

        #region GetProperty
        public T GetProperty<T>(string key)
        {
            // Check for nested properties
            string trail = null;
            int idx = key.IndexOf('.');

            if (idx > -1)
            {
                trail = key.Substring(idx + 1);
                key = key.Substring(0, idx);
            }

            // Get the value
            object value = null;
            PropertyInfo pi = wrappedObject.GetType().GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (pi != null)
            {
                value = pi.GetValue(
                    wrappedObject, null);
            }

            // Process the value
            if (value != null)
            {
                if (string.IsNullOrEmpty(trail))
                    return (T)value;
                else
                {
                    WrapperObject wrapper = new WrapperObject(value);
                    return wrapper.GetProperty<T>(trail);
                }
            }
            return default(T);
        } 
        #endregion // GetProperty

        #region SetProperty
        public void SetProperty(string key, object value)
        {
            // Check for nested properties
            int idx = key.LastIndexOf('.');

            object target = wrappedObject;
            if (idx > -1)
            {
                target = GetMember(key.Substring(0, idx));
                key = key.Substring(idx + 1);
            }

            // Get the value

            Type type = target.GetType();

            PropertyInfo pi = type.GetProperty(key, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (pi != null)
            {
                pi.SetValue(
                    target,
                    Helper.ConvertToType(value, pi.PropertyType),
                    null);

                if (target is SPField)
                {
                    SPField field = target as SPField;
                    field.Update();
                }
            }

        } 
        #endregion // SetProperty


        #region Static Methods
        public static T InstantiateObject<T>(string objectAdress)
        {
            return (T)Activator.CreateInstance(Type.GetType(objectAdress));
        }

        public static string GetPropertyAsString(object target, string property)
        {
            WrapperObject wrapper = new WrapperObject(target);

            object value = wrapper.GetProperty<object>(property);

            if (value == null)
                return null;
            else
                return value.ToString();
        } 
        #endregion // Static Methods
    }
}
